package com.googlecode.spoluyan.google.common;

import java.io.IOException;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

public class DateTypeAdapter extends TypeAdapter<Date> {
    public static final TypeAdapterFactory FACTORY = new TypeAdapterFactory() {
	@SuppressWarnings("unchecked")
	public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {
	    return typeToken.getRawType() == Date.class ? (TypeAdapter<T>) new DateTypeAdapter() : null;
	}
    };

    private static final DateFormat OUTPUT = buildFormat("yyyy-MM-dd'T'HH:mm:ss.mmm'Z'");
    private static final Set<DateFormat> POSSIBLE_FORMATS = new HashSet<DateFormat>() {
	private static final long serialVersionUID = -7507813260916041928L;

	{
	    add(OUTPUT);
	    add(buildFormat("EEE, d MMM yyyy HH:mm:ss Z"));
	    add(buildFormat("yyyy-MM-dd"));
	    add(buildFormat("yyyy-MM"));
	    add(buildFormat("yyyy"));
	}
    };

    private static DateFormat buildFormat(String f) {
	DateFormat format = new SimpleDateFormat(f, Locale.US);
	format.setTimeZone(TimeZone.getTimeZone(Defines.TIMEZONE));
	return format;
    }

    @Override
    public Date read(JsonReader in) throws IOException {
	if (in.peek() == JsonToken.NULL) {
	    in.nextNull();
	    return null;
	}
	return deserializeToDate(in.nextString());
    }

    private synchronized Date deserializeToDate(String json) {
	if (json == null) {
	    return null;
	}
	for (DateFormat df : POSSIBLE_FORMATS) {
	    try {
		Date d = df.parse(json);
		if (d != null) {
		    return d;
		}
	    } catch (ParseException e) {
	    }
	}

	return null;
    }

    @Override
    public synchronized void write(JsonWriter out, Date value) throws IOException {
	if (value == null) {
	    out.nullValue();
	    return;
	}
	String dateFormatAsString = OUTPUT.format(value);
	out.value(dateFormatAsString);
    }
}
